# ~*~ coding: utf-8 ~*~
import datetime
from flask import request, views, render_template, jsonify, current_app, send_from_directory, make_response
from flask_login import current_user

from apps.common.utils import fill_date_to_timestamp, list2dict, timestamp_obj_to_str
from apps.common.mixin import CompatibleWithSelect2Mixin
from apps.common import response
from apps.extensions import db
from apps.perms.mixin import PermissionRequiredMixin, LoginRequiredMixin
from ...models import CrontabSchedule, IntervalSchedule, PeriodicTask, PeriodicTaskRunHistory, Function
from ...forms import PeriodicTaskForm
from ...utils import create_or_update_periodic_task, delete_periodic_task
from ...tasks import run_periodic_task


__all__ = [
    'PeriodicTaskListAPI', 'PeriodicTaskListView', 'PeriodicTaskCreateView',
    'PeriodicTaskUpdateView', 'PeriodicTaskDeleteView', 'PeriodicTaskExecuteView',
    'PeriodicTaskHistoryListAPI', 'PeriodicTaskHistoryListView', 'PeriodicTaskHistoryLogAPI',
    'PeriodicTaskHistoryKillAPI', 'PeriodicTaskHistoryDeleteView'
]


class PeriodicTaskListAPI(CompatibleWithSelect2Mixin, PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-task-api'

    def get(self):
        create_time = request.args.get('create_time')
        start_time = end_time = None
        if create_time:
            start_time = fill_date_to_timestamp('start', create_time.split('~')[0])
            end_time = fill_date_to_timestamp('end', create_time.split('~')[1])

        filter_conditions = {
            'and': [
                ('create_time', 'gte', start_time),
                ('create_time', 'lte', end_time),
            ],
            'or': [
                ('name', 'like', request.args.get('search')),
            ],
        }

        # 排序
        sort = request.args.get('sort') or 'create_time'
        order = request.args.get('order') or 'desc'
        order_by_list = [(sort, order), ]

        # 分页
        offset = request.args.get('offset') or 0
        limit = request.args.get('limit') or 12

        # Compatible with select2
        if request.values.get('type') == 'select2':
            filter_conditions, order_by_list, offset, limit = self.select2_queryset()

        tasks, count = PeriodicTask.get_queryset(filter_conditions=filter_conditions,
                                                 order_by_list=order_by_list,
                                                 offset=offset, limit=limit)

        # 关联查询信息
        periodic_ids = [periodic.id for periodic in tasks]
        crontab_dict = db.session.query(PeriodicTask.id, CrontabSchedule). \
            join(CrontabSchedule, PeriodicTask.crontab_id == CrontabSchedule.id). \
            filter(PeriodicTask.id.in_(periodic_ids)). \
            all()
        crontab_dict = list2dict(crontab_dict)

        interval_dict = db.session.query(PeriodicTask.id, IntervalSchedule). \
            join(IntervalSchedule, PeriodicTask.interval_id == IntervalSchedule.id). \
            filter(PeriodicTask.id.in_(periodic_ids)). \
            all()
        interval_dict = list2dict(interval_dict)

        function_dict = db.session.query(PeriodicTask.id, Function.name). \
            join(Function, PeriodicTask.function_id == Function.id). \
            filter(PeriodicTask.id.in_(periodic_ids)). \
            all()
        function_dict = list2dict(function_dict)

        results = list()
        for task in tasks:
            results.append({
                'id': task.id,
                'name': task.name,
                'function': function_dict.get(task.id),
                'interval': interval_dict.get(task.id, '').__str__(),
                'crontab': crontab_dict.get(task.id, '').__str__(),
                'args': ','.join([str(v) for v in task.args]),
                'kwargs': ','.join(['{}={}'.format(k, str(v)) for k, v in task.kwargs.items()]),
                'enabled': task.enabled,
                'only_once': task.only_once,
                'description': task.description,
                'create_by': task.create_by,
                'create_time': timestamp_obj_to_str(task.create_time),
            })
        resp = {
            "data": {
                "rows": results,
                "total": count
            }
        }
        return jsonify(resp)


class PeriodicTaskListView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-task-list'

    def get(self):
        return render_template('tasks/periodic/task_list.html')


class PeriodicTaskUpdateView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-task-update'

    def get(self, pk):
        instance = db.session.query(PeriodicTask).get(pk)
        content = {
            'id': instance.id,
            'name': instance.name,
            'function_id': instance.function_id,
            'interval_id': instance.interval_id,
            'crontab_id': instance.crontab_id,
            'args': instance.args,
            'kwargs': instance.kwargs.items(),
            'enabled': instance.enabled,
            'only_once': instance.only_once,
            'description': instance.description,
        }
        return render_template('tasks/periodic/task_update.html', **content)

    def post(self, pk):
        form = PeriodicTaskForm(request.values)
        instance = db.session.query(PeriodicTask).get(pk)
        if form.validate():
            _clean_data = form.data
            if instance.name != _clean_data.get('name'):
                if db.session.query(PeriodicTask).filter_by(name=_clean_data.get('name')).count():
                    return response.params_error({
                        'field': 'name',
                        'message': '任务名称不能重复'
                    })

            _clean_data['create_by'] = current_user.name
            task = instance.update(**_clean_data)

            crontab = db.session.query(CrontabSchedule). \
                filter(CrontabSchedule.id == task.crontab_id). \
                first()
            interval = db.session.query(IntervalSchedule). \
                filter(IntervalSchedule.id == task.interval_id). \
                first()
            create_or_update_periodic_task(task, interval=interval, crontab=crontab)
            return response.success()
        else:
            return response.params_error(form.get_error())


class PeriodicTaskCreateView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-task-create'

    def get(self):
        return render_template('tasks/periodic/task_create.html')

    def post(self):
        form = PeriodicTaskForm(request.values)
        if form.validate():
            _clean_data = form.data
            if db.session.query(PeriodicTask).filter_by(name=_clean_data.get('name')).count():
                return response.params_error({
                    'field': 'name',
                    'message': '任务名称不能重复'
                })

            _clean_data['create_by'] = current_user.name
            periodic = PeriodicTask.create(**_clean_data)

            crontab = db.session.query(CrontabSchedule).\
                filter(CrontabSchedule.id == periodic.crontab_id).\
                first()
            interval = db.session.query(IntervalSchedule).\
                filter(IntervalSchedule.id == periodic.interval_id).\
                first()
            create_or_update_periodic_task(periodic, interval=interval, crontab=crontab)
            return response.success()
        else:
            return response.params_error(form.get_error())


class PeriodicTaskDeleteView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-task-delete'

    def post(self, pk):
        instance = db.session.query(PeriodicTask).get(pk)
        instance.delete(commit=False)
        db.session.query(PeriodicTaskRunHistory).filter(PeriodicTaskRunHistory.task_id == instance.id).delete(synchronize_session=False)
        db.session.commit()
        delete_periodic_task(instance)
        return response.success()


class PeriodicTaskExecuteView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-task-execute'

    def post(self, pk):
        instance = db.session.query(PeriodicTask).get(pk)
        if instance.only_once and instance.has_running:
            return response.auth_error(data={
                'message': '该任务开启了"存活一个"功能，目前已有任务执行'
            })
        run_periodic_task.apply_async(args=(instance.id,), serializer='json')
        return response.success()


# history


class PeriodicTaskHistoryListAPI(CompatibleWithSelect2Mixin, PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-history-list'

    def get(self):
        date_start = request.args.get('create_time')
        start_time = end_time = None
        if date_start:
            start_time = fill_date_to_timestamp('start', date_start.split('~')[0])
            end_time = fill_date_to_timestamp('end', date_start.split('~')[1])

        search = request.args.get('search')
        task_ids = list()
        if search:
            task_ids = db.session.query(PeriodicTask.id).filter(PeriodicTask.name.like('%%%s%%' % search)).all()
            try:
                task_ids = list(zip(*task_ids))[0]
            except IndexError:
                task_ids = list()

        filter_conditions = {
            'and': [
                ('date_start', 'gte', start_time),
                ('date_start', 'lte', end_time),
                ('is_success', 'eq', request.args.get('is_success') or None),
            ],
            'or': [
                # ('name', 'like', request.args.get('search')),
                ('task_id', 'in', task_ids) if task_ids else ()
            ],
        }

        # 排序
        sort = request.args.get('sort')
        order = request.args.get('order')
        order_by_list = [(sort, order), ]

        # 分页
        offset = request.args.get('offset') or 0
        limit = request.args.get('limit') or 12

        # 根据权限查询过滤
        queryset = None
        if request.args.get('task'):
            task_id = request.args.get('task')
            queryset = db.session.query(PeriodicTaskRunHistory).filter(PeriodicTaskRunHistory.task_id == task_id)

        # Compatible with select2
        if request.values.get('type') == 'select2':
            filter_conditions, order_by_list, offset, limit = self.select2_queryset()

        histories, count = PeriodicTaskRunHistory.get_queryset(
            queryset=queryset,
            filter_conditions=filter_conditions,
            order_by_list=order_by_list,
            offset=offset,
            limit=limit
        )
        # 关联查询信息
        history_ids = [history.id for history in histories]
        task_dict = db.session.query(PeriodicTaskRunHistory.id, PeriodicTask.name). \
            join(PeriodicTask, PeriodicTaskRunHistory.task_id == PeriodicTask.id). \
            filter(PeriodicTaskRunHistory.id.in_(history_ids)). \
            all()
        task_dict = list2dict(task_dict)

        results = list()
        for history in histories:
            results.append({
                'id': history.id,
                'task': task_dict.get(history.id),
                'date_start': timestamp_obj_to_str(history.date_start),
                'date_finished': timestamp_obj_to_str(history.date_finished),
                'timedelta': str(datetime.timedelta(seconds=history.timedelta)) if history.timedelta else '-',
                'is_finished': history.is_finished,
                'is_success': history.is_success,
                'result': history.result[0:15] if history.result else '',
                'log_path': history.log_path,
            })
        resp = {
            "data": {
                "rows": results,
                "total": count
            }
        }
        return jsonify(resp)


class PeriodicTaskHistoryListView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-history-list'

    def get(self):
        return render_template('tasks/periodic/task_history.html')


class PeriodicTaskHistoryLogAPI(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-history-list'

    def get(self, filename):
        resp = make_response(send_from_directory(current_app.config.get('TASKS_LOG_DIR'), filename))
        resp.headers["Content-type"] = "application/json;charset=UTF-8"
        return resp


class PeriodicTaskHistoryKillAPI(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-history-kill'

    def post(self, pk):
        instance = db.session.query(PeriodicTaskRunHistory).get(pk)
        if instance.is_finished in [0, 1]:
            return response.params_error(data={
                'message': '该定时任务已经执行完成，无法终止'
            })
        instance.kill()
        return response.success()


class PeriodicTaskHistoryDeleteView(PermissionRequiredMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'tasks-history-delete'

    def post(self, pk):
        instance = db.session.query(PeriodicTaskRunHistory).get(pk)
        instance.delete()
        return response.success()
